/*    
    OpenMheg - A free MHEG-5 library
    Copyright (C) 2008 Dharmesh Malam

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Collections;

using Core.Model;
using Core.Model.Ingredients.Presentable.Visibles;


namespace Engine
{
    public class MHEGDisplayStack : IEnumerable
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType );

        private Object lockObj = new Object();

        private List<Visible> displayStack = new List<Visible>( 32 );
        private bool screenLocked = false;

        public delegate void RedrawAreasHandler ( params System.Drawing.Rectangle [] rs );
        public delegate void RedrawAreaHandler ( Point boxPosition, Size boxSize );
        public delegate void RedawHandler ();
        protected event RedrawAreasHandler redrawAreas;
        protected event RedrawAreaHandler redrawArea;
        protected event RedawHandler redraw;



        public MHEGDisplayStack ()
        {
            redrawAreas += new RedrawAreasHandler( RedrawAreas );
            redrawArea += new RedrawAreaHandler( RedrawArea );
            redraw += new RedawHandler( Redraw );
        }


        public List<Visible> DrawingStack
        {

            get
            {
                lock ( lockObj )
                {
                    List<Visible> ret = new List<Visible>( displayStack );
                    foreach ( Visible v in Stack )
                    {
                        if ( v.RunningStatus )
                        {

                            ret.Add( ( Visible ) v.Clone() );
                        }
                    }

                    return ret;
                }
            }
        }
        protected void OnRedrawAreas ( params System.Drawing.Rectangle [] rs )
        {
            redrawAreas.BeginInvoke( rs, null, null );
            //redrawAreas( rs );
        }
        protected void OnRedrawArea ( Point boxPosition, Size boxSize )
        {
            redrawArea.BeginInvoke( boxPosition, boxSize, null, null );
            //redrawArea( boxPosition , boxSize );
        }

        protected void OnRedraw ()
        {
            redraw.BeginInvoke( null, null );
            //redraw();
        }

        private Queue<System.Drawing.Rectangle> invalidArea = new Queue<System.Drawing.Rectangle>();

        private System.Drawing.Rectangle entireScreen = Engine.MHEGConstants.ScreenArea;

        public bool ScreenLocked
        {
            get { return screenLocked; }

            private set
            {
                screenLocked = value;

                if ( !screenLocked )
                    Redraw();
            }
        }

        public List<Visible> Stack
        {
            get
            {
                lock ( lockObj )
                {

                    return displayStack;
                }
            }

            private set { displayStack = value; }
        }


        internal void Add ( Visible visible )
        {
            if ( !displayStack.Contains( visible ) )
                displayStack.Add( visible );
        }

        internal void Remove ( Visible visible )
        {
            displayStack.Remove( visible );
        }

        internal void Clear ()
        {
            displayStack.Clear();
            invalidArea.Clear();
            invalidArea.Enqueue( Engine.MHEGConstants.ScreenArea );
        }

        internal void Redraw ()
        {
            lock ( lockObj )
            {
                invalidArea.Clear();
                invalidArea.Enqueue( entireScreen );
            }
        }

        internal void RedrawArea ( Point boxPosition, Size boxSize )
        {
            lock ( lockObj )
            {
                invalidArea.Enqueue( new System.Drawing.Rectangle( boxPosition, boxSize ) );

            }
        }

        internal void RedrawAreas ( params System.Drawing.Rectangle [] rs )
        {
            lock ( lockObj )
            {
                foreach ( System.Drawing.Rectangle r in rs )
                {

                    invalidArea.Enqueue( r );
                }
            }
        }


        internal System.Drawing.Rectangle [] Flush ()
        {
            CompactQ();
            if ( invalidArea.Count >0 )
            {
                
                System.Drawing.Rectangle [] ret = invalidArea.ToArray();
                
                
                invalidArea.Clear();
                return ret;
            }
            else
                return null;
        }

        private void CompactQ ()
        {

        }


        public void BringToFront ( Visible visible )
        {
            if ( Stack.Contains( visible ) )
            {
                Stack.Remove( visible );
                Stack.Add( visible );

                if ( visible.RunningStatus )
                    OnRedrawArea( visible.BoxPosition, visible.BoxSize );
            }
        }

        public void SendToBack ( Visible visible )
        {
            if ( Stack.Contains( visible ) )
            {
                Stack.Remove( visible );
                Stack.Insert( 0, visible );

                if ( visible.RunningStatus )
                    OnRedrawArea( visible.BoxPosition, visible.BoxSize );
            }
        }

        public void PutBefore ( Visible infront, Visible behind )
        {
            if ( !infront.Equals( behind ) && Stack.Contains( infront ) && Stack.Contains( behind ) )
            {
                Stack.Remove( infront );
                Stack.Insert( Stack.IndexOf( behind ) + 1, infront );

                if ( behind.RunningStatus && infront.RunningStatus)
                    OnRedrawAreas( new System.Drawing.Rectangle( behind.BoxPosition, behind.BoxSize ),
                                 new System.Drawing.Rectangle( infront.BoxPosition, infront.BoxSize ) );

            }
        }

        public void PutBehind ( Visible behind, Visible infront )
        {
            if ( !behind.Equals( infront ) && Stack.Contains( behind ) && Stack.Contains( infront ) )
            {
                Stack.Remove( behind );
                Stack.Insert( Stack.IndexOf( infront ), behind );

                if ( infront.RunningStatus && behind.RunningStatus)
                    OnRedrawAreas( new System.Drawing.Rectangle( behind.BoxPosition, behind.BoxSize ),
                                 new System.Drawing.Rectangle( infront.BoxPosition, infront.BoxSize ) );

            }
        }


        internal void LockScreen ()
        {
           // log.Debug( "Locking Screen" );
            ScreenLocked = true;
        }

        internal void UnlockScreen ()
        {
            //log.Debug( "Unlocking Screen" );
            ScreenLocked = false;
        }



        #region IEnumerable Members

        public IEnumerator GetEnumerator ()
        {
            return displayStack.GetEnumerator();
        }

        #endregion

        public override string ToString ()
        {
            return displayStack.ToString();
        }

        public override int GetHashCode ()
        {
            return displayStack.GetHashCode();
        }

        public override bool Equals ( object obj )
        {
            if ( obj is MHEGDisplayStack )
                return ( ( MHEGDisplayStack ) obj ).Stack.Equals( displayStack );
            else
                return false;
        }

    }
}
